#include <sqlite_orm/sqlite_orm.h>

#include <filesystem>
#include <string>

#include "CommonTestSuite.hpp"
#include "TemplateLibrary.h"
#include "TemplateRelation.h"
#include "TemplateVersionInfo.h"
#include "common_utils.hpp"
#include "dBOperations.hpp"
#include "logger.hpp"
#include "storageManager.hpp"

using namespace InoIDE;
using namespace sqlite_orm;
using namespace FA_TL;

struct TemplateNodeStorage {
    static auto makeTable(const std::filesystem::path &dbPath,
                          const std::string &tableName) {
        return make_storage(
            dbPath.string(),
            make_table(
                tableName,
                make_column("id", &TemplateLibrary::id,
                            primary_key().autoincrement()),
                make_column("name", &TemplateLibrary::name),
                make_column("_order", &TemplateLibrary::_order),
                make_column("nodeType", &TemplateLibrary::nodeType),
                make_column("parentId", &TemplateLibrary::parentId),
                make_column("nodeStatus", &TemplateLibrary::nodeStatus),
                make_column("instanceTimes", &TemplateLibrary::instanceTimes),
                make_column("deleteFlag", &TemplateLibrary::deleteFlag),
                make_column("extendParam", &TemplateLibrary::extendedParam)));
    }
};

struct TemplateRelationStorage {
    static auto makeTable(const std::filesystem::path &dbPath,
                          const std::string &tableName) {
        return make_storage(
            dbPath.string(),
            make_table(
                tableName,
                make_column("id", &TemplateRelation::id,
                            primary_key().autoincrement()),
                make_column("_order", &TemplateRelation::_order),
                make_column("treeNodeId", &TemplateRelation::treeNodeId),
                make_column("treeNodeVersion",
                            &TemplateRelation::treeNodeVersion),
                make_column("referenceId", &TemplateRelation::referenceId),
                make_column("referenceVersion",
                            &TemplateRelation::referenceVersion),
                make_column("count", &TemplateRelation::count),
                make_column("deleteFlag", &TemplateRelation::deleteFlag)));
    }
};

struct TemplateVersionInfoStorage {
    static auto makeTable(const std::filesystem::path &dbPath,
                          const std::string &tableName) {
        using namespace sqlite_orm;
        return make_storage(
            dbPath.string(),
            make_table(
                tableName, make_column("id", &TemplateVersionInfo::id),
                make_column("version", &TemplateVersionInfo::version),
                make_column("guid", &TemplateVersionInfo::guid),
                make_column("versionStr", &TemplateVersionInfo::versionStr),
                make_column("templateDataId",
                            &TemplateVersionInfo::templateDataId),
                make_column("type", &TemplateVersionInfo::type),
                make_column("isDefault", &TemplateVersionInfo::isDefault),
                make_column("nodeStatus", &TemplateVersionInfo::nodeStatus),
                make_column("checkStatus", &TemplateVersionInfo::checkStatus),
                make_column("instanceTimes",
                            &TemplateVersionInfo::instanceTimes),
                make_column("extendParam", &TemplateVersionInfo::extendParam),
                make_column("deleteFlag", &TemplateVersionInfo::deleteFlag),
                primary_key(&TemplateVersionInfo::id,
                            &TemplateVersionInfo::version)));
    }
};
// 创建storage
using NodeStorageType = decltype(TemplateNodeStorage::makeTable("", ""));

// 使用示例,以模板库为例
TEST_F(CommonTestSuite, TestOperator) {
    auto storage = TemplateNodeStorage::makeTable("", "");
    auto syn = storage.pragma.synchronous();
    storage.pragma.synchronous(2);

    // 创建storage
    using NodeStorageType = decltype(TemplateNodeStorage::makeTable("", ""));

    // 创建数据库操作对象
    auto nodeStorage = makeDbOperations<TemplateLibrary, TemplateNodeStorage>(
        "./test.db", TemplateLibrary::tableName);

    // 单个插入
    TemplateLibrary node;
    node.name = "John";
    int64_t newNodeId = nodeStorage.insert(node);

    // 批量插入
    std::vector<TemplateLibrary> nodes = {{}, {}, {}};

    auto insertedIds = nodeStorage.batchInsert(nodes);

    // 通过ID查询
    auto foundUser = nodeStorage.getById(newNodeId);
    if (foundUser) {
        LOG_INFO("Found user: {}", foundUser->name);
    }

    // 更新用户
    if (foundUser) {
        foundUser->name = "John Smith";
        nodeStorage.updateById(*foundUser);
    }

    // 条件查询
    auto johnsOnly = nodeStorage.findWhere(
        sqlite_orm::like(&TemplateLibrary::name, "John%"));

    // 计数
    int totalUsers = nodeStorage.count();

    // 条件计数
    int johnCount = nodeStorage.countWhere(
        sqlite_orm::like(&TemplateLibrary::name, "John%"));

    // 删除用户
    nodeStorage.deleteById(newNodeId);

    // 批量删除
    nodeStorage.batchDeleteByIds(insertedIds);
}

NodeStorageType make_storage_for_thread() {
    // 创建数据库操作对象
    auto st = TemplateNodeStorage::makeTable("./test.db", "testconfig");

    st.open_forever();

    /* ① 切到 WAL；只需要做一次，也可以放在主线程里做 */
    st.pragma.journal_mode(journal_mode::WAL);

    /* ② 保持 NORMAL 锁模式（WAL 强制是 NORMAL，其实不用写） */
    st.pragma.locking_mode(locking_mode::NORMAL);

    /* ③ 等待 5 秒再放弃锁，而不是立即返回 SQLITE_BUSY */
    st.pragma.busy_timeout(5000);

    /* 可选：为了吞吐量把同步级别放低一点 */
    st.pragma.synchronous(2);

    LOG_INFO("Busy timeout is {}", st.pragma.busy_timeout());
    return st;
}

std::mutex cout_mutex;  // 用于保护 std::cout 的互斥锁

bool worker() {
    try {
        {
            std::lock_guard<std::mutex> lock(cout_mutex);  // 获取锁
            LOG_INFO("Thread ID: {}", std::this_thread::get_id());
        }
        auto st = make_storage_for_thread();
        st.transaction([&] {
            TemplateLibrary node;
            node.name = "John";
            int64_t newNodeId = st.insert(node);
            {
                std::lock_guard<std::mutex> lock(cout_mutex);  // 获取锁
                LOG_INFO("last insert id is {}", st.last_insert_rowid());
            }
            return true;
        });
        return true;
    }

    catch (const std::exception &e) {
        std::lock_guard<std::mutex> lock(cout_mutex);  // 获取锁
        LOG_ERROR("Async task error: {}", e.what());
        return false;
    }
}

#include <future>
/// 数据库连接配置测试
/// 配置超时时间之后,多线程打开数据库时不在报错
TEST_F(CommonTestSuite, TestConfig1) {
    auto start_time = std::chrono::high_resolution_clock::now();
    auto st = TemplateNodeStorage::makeTable("./test.db", "testconfig");
    /* 第一次启动时建表 */
    st.sync_schema();

    std::vector<std::future<bool>> futures;
    // 使用 emplace_back 直接在 vector 中构造 std::future 对象
    futures.emplace_back(std::async(std::launch::async, worker));
    futures.emplace_back(std::async(std::launch::async, worker));
    futures.emplace_back(std::async(std::launch::async, worker));
    futures.emplace_back(std::async(std::launch::async, worker));
    futures.emplace_back(std::async(std::launch::async, worker));

    for (int i = 0; i < 5; ++i) {
        futures[i].get();
    }

    auto end_time = std::chrono::high_resolution_clock::now();
    auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(
                        end_time - start_time)
                        .count();
    LOG_INFO("Thread ID: {}", std::this_thread::get_id());
    LOG_INFO("Total duration: {} ms", duration);
    LOG_INFO("================================");
}

std::shared_ptr<NodeStorageType> make_storage_with_TimeOut() {
    // 创建数据库操作对象
    // auto st = TemplateNodeStorage::makeTable("./test.db", "testconfig");
    std::shared_ptr<NodeStorageType> stptr = std::make_shared<NodeStorageType>(
        TemplateNodeStorage::makeTable("./test.db", "testconfig"));

    stptr->open_forever();

    /* ① 切到 WAL；只需要做一次，也可以放在主线程里做 */
    stptr->pragma.journal_mode(journal_mode::WAL);

    /* ② 保持 NORMAL 锁模式（WAL 强制是 NORMAL，其实不用写） */
    stptr->pragma.locking_mode(locking_mode::NORMAL);

    /* ③ 等待 2 秒再放弃锁，而不是立即返回 SQLITE_BUSY */
    stptr->pragma.busy_timeout(2000);

    stptr->pragma.synchronous(2);

    LOG_INFO("before busy timeout is {}", stptr->pragma.busy_timeout());
    return stptr;
}

/// 插入持续1s
bool timeWorker() {
    try {
        auto st = make_storage_with_TimeOut();
        LOG_INFO("after busy timeout is {}", st->pragma.busy_timeout());
        auto start_time = std::chrono::high_resolution_clock::now();

        int cout = 0;
        st->transaction([&] {
            LOG_INFO("start transaction");
            auto tran_start_time = std::chrono::high_resolution_clock::now();
            TemplateLibrary node;
            node.name = "John";
            int64_t newNodeId = st->insert(node);

            // 持续插入数据，直到达到1秒
            while (
                std::chrono::duration_cast<std::chrono::seconds>(
                    std::chrono::high_resolution_clock::now() - tran_start_time)
                    .count() < 1) {
                newNodeId = st->insert(node);
                cout++;
            }
            LOG_INFO("end transaction");
            return true;
        });

        auto end_time = std::chrono::high_resolution_clock::now();
        auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(
                            end_time - start_time)
                            .count();

        auto start_time_duration =
            std::chrono::duration_cast<std::chrono::milliseconds>(
                start_time.time_since_epoch())
                .count();
        int hours = start_time_duration / (1000 * 60 * 60);
        int minutes = (start_time_duration % (1000 * 60 * 60)) / (1000 * 60);
        int seconds = (start_time_duration % (1000 * 60)) / 1000;
        int milliseconds = start_time_duration % 1000;

        {
            std::lock_guard<std::mutex> lock(cout_mutex);
            LOG_INFO("Thread ID: {}", std::this_thread::get_id());
            LOG_INFO("Start time: {}h {}m {}s {}ms", hours, minutes, seconds,
                     milliseconds);
            LOG_INFO("Total duration: {} ms", duration);
            LOG_INFO("Total insertions: {}", cout);
        }
        return true;
    } catch (const std::exception &e) {
        LOG_ERROR("Async task error: {}", e.what());
        return false;
    }
}

TEST_F(CommonTestSuite, TestTimeOut) {
    auto start_time = std::chrono::high_resolution_clock::now();
    std::vector<std::future<bool>> futures;
    futures.emplace_back(std::async(std::launch::async, timeWorker));
    futures.emplace_back(std::async(std::launch::async, timeWorker));
    futures.emplace_back(std::async(std::launch::async, timeWorker));
    futures.emplace_back(std::async(std::launch::async, timeWorker));
    futures.emplace_back(std::async(std::launch::async, timeWorker));

    for (int i = 0; i < 5; ++i) {
        futures[i].get();
    }
    auto end_time = std::chrono::high_resolution_clock::now();
    auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(
                        end_time - start_time)
                        .count();

    LOG_INFO("Thread ID: {}", std::this_thread::get_id());
    LOG_INFO("Total duration: {} ms", duration);
}

std::shared_ptr<NodeStorageType> make_storage_with_busyHandle() {
    // 创建数据库操作对象
    // auto st = TemplateNodeStorage::makeTable("./test.db", "testconfig");
    std::shared_ptr stptr = std::make_shared<NodeStorageType>(
        TemplateNodeStorage::makeTable("./test.db", "testconfig"));

    stptr->open_forever();

    /* ① 切到 WAL；只需要做一次，也可以放在主线程里做 */
    stptr->pragma.journal_mode(journal_mode::WAL);

    /* ② 保持 NORMAL 锁模式（WAL 强制是 NORMAL，其实不用写） */
    stptr->pragma.locking_mode(locking_mode::NORMAL);

    stptr->busy_handler([](int retry_count) -> bool {
        LOG_INFO("Thread ID: {} Waiting... Attempt {}",
                 std::this_thread::get_id(), retry_count);
        std::this_thread::sleep_for(std::chrono::milliseconds(100));
        return true;  // 继续等待
    });

    stptr->pragma.synchronous(2);
    LOG_INFO("before busy timeout is {}", stptr->pragma.busy_timeout());
    return stptr;
}

/// 每个事务插入100000条数据
bool Insert1000() {
    try {
        auto st = make_storage_with_busyHandle();
        auto start_time = std::chrono::high_resolution_clock::now();

        int cout = 0;
        st->transaction([&] {
            LOG_INFO("Thread ID: {} start transaction",
                     std::this_thread::get_id());
            TemplateLibrary node;
            node.name = "John";
            int64_t newNodeId = st->insert(node);

            // 持续插入数据，直到达到1000条
            while (cout < 100000) {
                newNodeId = st->insert(node);
                cout++;
            }
            LOG_INFO("Thread ID: {} end transaction",
                     std::this_thread::get_id());
            return true;
        });

        auto end_time = std::chrono::high_resolution_clock::now();
        auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(
                            end_time - start_time)
                            .count();

        auto start_time_duration =
            std::chrono::duration_cast<std::chrono::milliseconds>(
                start_time.time_since_epoch())
                .count();
        int hours = start_time_duration / (1000 * 60 * 60);
        int minutes = (start_time_duration % (1000 * 60 * 60)) / (1000 * 60);
        int seconds = (start_time_duration % (1000 * 60)) / 1000;
        int milliseconds = start_time_duration % 1000;

        {
            std::lock_guard<std::mutex> lock(cout_mutex);

            LOG_INFO("Thread ID: {} Start time: {}h {}m {}s {}ms",
                     std::this_thread::get_id(), hours, minutes, seconds,
                     milliseconds);
            LOG_INFO("Thread ID: {} Total duration: {} ms",
                     std::this_thread::get_id(), duration);
            LOG_INFO("Thread ID: {} Total insertions: {}",
                     std::this_thread::get_id(), cout);
        }
        return true;
    } catch (const std::exception &e) {
        LOG_ERROR("Async task error: {}", e.what());
        return false;
    }
}

TEST_F(CommonTestSuite, TestBusyHandle) {
    auto start_time = std::chrono::high_resolution_clock::now();
    std::vector<std::future<bool>> futures;
    futures.emplace_back(std::async(std::launch::async, Insert1000));
    futures.emplace_back(std::async(std::launch::async, Insert1000));
    futures.emplace_back(std::async(std::launch::async, Insert1000));
    futures.emplace_back(std::async(std::launch::async, Insert1000));
    futures.emplace_back(std::async(std::launch::async, Insert1000));

    for (int i = 0; i < 5; ++i) {
        futures[i].get();
    }
    auto end_time = std::chrono::high_resolution_clock::now();
    auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(
                        end_time - start_time)
                        .count();

    LOG_INFO("Thread ID: {}", std::this_thread::get_id());
    LOG_INFO("Total duration: {} ms", duration);
}

TEST_F(CommonTestSuite, TestMulInsert) {
    std::mutex g_cout_mutex;
    auto nodeStorage =
        makeDbOperations<TemplateLibrary, TemplateNodeStorage>("./test.db");

    // 验证数据库是否支持多线程

    nodeStorage.deleteAll();
    auto func = [&g_cout_mutex]() {
        for (int i = 0; i < 100; ++i) {
            // 创建数据库操作对象
            auto nodeStorage =
                makeDbOperations<TemplateLibrary, TemplateNodeStorage>(
                    "./test.db");
            TemplateLibrary node;
            node.name = "John";
            for (int i = 0; i < 100000; ++i) {
                int64_t newNodeId = nodeStorage.insert(node);
            }

            int totalUsers = nodeStorage.count();
            {
                std::lock_guard<std::mutex> lock(g_cout_mutex);
                LOG_INFO("thread id is {} current Count: {}",
                         std::this_thread::get_id(), totalUsers);
            }
            return;
        }
    };

    std::vector<std::thread> threadPool;
    threadPool.reserve(10);
    for (int i = 0; i < 10; ++i) {
        threadPool.emplace_back(func);
    }

    for (int i = 0; i < 10; ++i) {
        if (threadPool[i].joinable()) {
            threadPool[i].join();
        }
    }

    LOG_INFO("All 10 threads have finished.");
    {
        std::lock_guard<std::mutex> lock(g_cout_mutex);
        int totalUsers = nodeStorage.count();
        LOG_INFO("thread id is {} finish Count: {}", std::this_thread::get_id(),
                 totalUsers);
    }

    return;
}

TEST_F(CommonTestSuite, CloseAll) {
    // 创建数据库操作对象
    auto nodeStorage =
        makeDbOperations<TemplateLibrary, TemplateNodeStorage>("./test.db");
    auto relationStorage =
        makeDbOperations<TemplateRelation, TemplateRelationStorage>(
            "./test.db");
    auto versionStorage =
        makeDbOperations<TemplateVersionInfo, TemplateVersionInfoStorage>(
            "./test.db");
    StorageManInstance().clear();
}

TEST_F(CommonTestSuite, FlushCache) {}